#include "aceMesh_utils.h"
// #include "args.h"
#include <sys/time.h>

#include "am_machine.h"
#ifdef TARG_SW9
extern __uncached volatile unsigned long con_status;
extern __uncached volatile unsigned long con_dest_ptr;
// extern __uncached volatile unsigned long con_graph;
// extern __uncached volatile int spawn_status[MULTI_STATUS];
extern __uncached volatile unsigned long con_src_ptr;
extern __uncached volatile unsigned long con_task_ptr[BLOCK_SIZE];
extern __uncached volatile unsigned long temp_task[MULTI_TASK];
extern __uncached volatile unsigned long con_task_num;
extern __uncached volatile unsigned long end_head, end_tail;
extern int cgid;
extern int total_num_threads;
extern __uncached unsigned long block_time, block_temp;
// extern __uncached volatile unsigned long dest_task[MULTI_TASK], dest_head, dest_tail;
extern __uncached volatile unsigned long main_task[BLOCK_SIZE], main_head, main_tail;
#ifdef GS_AGENT
#define thread_id 0
#else
#define thread_id total_num_threads - 1
#endif

#endif

long long tick_count_now()
{
    long long my_count;
    struct timeval start;
    gettimeofday(&start, NULL);
    my_count = start.tv_sec + start.tv_usec;
    return my_count;
}

unsigned long rpcc()
{
    unsigned long time;
    asm("rtc %0" : "=&r"(time)::"memory");
    return time;
}

//! Table of primes used by fast random-number generator (FastRandom).
/** Also serves to keep anything else from being placed in the same
    cache line as the global data items preceding it. */
static const unsigned Primes[] = {
    0x9e3779b1, 0xffe6cc59, 0x2109f6dd, 0x43977ab5, 0xba5703f5, 0xb495a877, 0xe1626741, 0x79695e6b,
    0xbc98c09f, 0xd5bee2b3, 0x287488f9, 0x3af18231, 0x9677cd4d, 0xbe3a6929, 0xadc6a877, 0xdcf0674b,
    0xbe4d6fe9, 0x5f15e201, 0x99afc3fd, 0xf3f16801, 0xe222cfff, 0x24ba5fdb, 0x0620452d, 0x79f149e3,
    0xc8b93f49, 0x972702cd, 0xb07dd827, 0x6c97d5ed, 0x085a3d61, 0x46eb5ea7, 0x3d9910ed, 0x2e687b5b,
    0x29609227, 0x6eb081f1, 0x0954c4e1, 0x9d114db9, 0x542acfa9, 0xb3e6bd7b, 0x0742d917, 0xe9f3ffa7,
    0x54581edb, 0xf2480f45, 0x0bb9288f, 0xef1affc7, 0x85fa0ca7, 0x3ccc14db, 0xe6baf34b, 0x343377f7,
    0x5ca19031, 0xe6d9293b, 0xf0a9f391, 0x5d2e980b, 0xfc411073, 0xc3749363, 0xb892d829, 0x3549366b,
    0x629750ad, 0xb98294e5, 0x892d9483, 0xc235baf3, 0x3d2402a3, 0x6bdef3c9, 0xbec333cd, 0x40c9520f};

unsigned GetPrime(unsigned seed) { return Primes[seed % (sizeof(Primes) / sizeof(Primes[0]))]; }

void my_mutex_lock(volatile int *my_lock)
{
#ifdef TARG_SW5
#ifdef MUTEX_BASIC
    unsigned int __addr, __tmpi;
    __asm__ __volatile__(
        "        memb\n"
        "        ldi      %1,%2\n"
        "1:      ldw_inc  %0,0(%1)\n"
        "        bne      %0,1b\n"
        "        memb\n"
        : "=&r"(__tmpi), "=&r"(__addr)
        : "m"(*my_lock)
        : "memory");
    return;
#else
    unsigned int __tmp = 0;
    unsigned int __cnt;
    __asm__ __volatile__(
        "0:     ldw     %[__tmp], %[my_lock]\n"
        "       beq     %[__tmp], 2f\n"
        "       ldi     %[__cnt], 50\n"
        "       sll     %[__cnt], 4, %[__cnt]\n"
        "1:     subw    %[__cnt], 1, %[__cnt]\n"
        "       bne     %[__cnt], 1b\n"
        "       br      0b\n"
        "2:     ldw_inc    %[__tmp], %[my_lock]\n"
        "       bne     %[__tmp], 0b\n"
        "       memb    \n"
        "       br      3f\n"
        "3:     unop    \n"
        : [__tmp] "=&r"(__tmp), [__cnt] "=&r"(__cnt)
        : [my_lock] "m"(*(my_lock))
        : "memory");
    return;
#endif
#elif TARG_SW9
   
   my_h2ldm(con_src_ptr, thread_id, cgid, unsigned long) = (unsigned long)my_lock;
    // con_src_ptr=(unsigned long)my_lock;
    asm volatile("memb\n");
    my_h2ldm(con_status, thread_id, cgid, int) = 1;
    // my_h2ldm(con_status,total_num_threads-1,cgid,int)=1;
    asm volatile("memb\n");
    block_temp = rpcc();
    while (my_h2ldm(con_status, thread_id, cgid, int))
        ;
    block_time += rpcc() - block_temp;
    asm volatile("memb\n");
    return;
    

#endif
}
void my_mutex_unlock(volatile int *my_lock)
{
#ifdef TARG_SW5
#ifdef MUTEX_BASIC
    unsigned int __addr;
    __asm__ __volatile__(
        "        memb\n"
        "        ldi      %0,%1\n"
        "        stw  $31,0(%0)\n"
        "        memb\n"
        : "=&r"(__addr)
        : "m"(*my_lock)
        : "memory");
    return;
#else
    unsigned int __tmp = 0;
    __asm__ __volatile__(
        "       memb    \n"
        "       mov     0, %[__tmp]\n"
        "       stw     %[__tmp], %[my_lock]\n"
        : [__tmp] "=&r"(__tmp)
        : [my_lock] "m"(*(my_lock))
        : "memory");
    return;
#endif

#elif TARG_SW9
    // my_h2ldm(con_src_ptr, thread_id, cgid, unsigned long) = (unsigned long)my_lock;
    // // con_src_ptr=(unsigned long)my_lock;
    // asm volatile("memb\n");
    // // con_status=4;
    // my_h2ldm(con_status, thread_id, cgid, int) = 4;
    // asm volatile("memb\n");
    // block_temp = rpcc();
    // while (my_h2ldm(con_status, thread_id, cgid, int))
    //     ;
    // block_time += rpcc() - block_temp;
    // asm volatile("memb\n");
    // return;
     unsigned int __tmp = 0;
    __asm__ __volatile__(
        "       memb    \n"
        "       mov     0, %[__tmp]\n"
        "       stl     %[__tmp], %[my_lock]\n"
        : [__tmp] "=&r"(__tmp)
        : [my_lock] "m"(*(my_lock))
        : "memory");
    return;
#endif
}
#ifdef TARG_SW9
void my_atomic_add(volatile int *ref)
{
    // con_dest_ptr=(unsigned long)ref;
    my_h2ldm(con_src_ptr, thread_id, cgid, unsigned long) = (unsigned long)ref;
    asm volatile("memb\n");
    // con_status=2;
    my_h2ldm(con_status, thread_id, cgid, int) = 2;
    asm volatile("memb\n");
    block_temp = rpcc();
    while (my_h2ldm(con_status, thread_id, cgid, int))
        ;
    block_time += rpcc() - block_temp;
    asm volatile("memb\n");
    return;
}
#ifndef MULTI_SPAWN
void my_spawn_to_id(struct task *t)
{
#else
void my_spawn_to_id(int num)
{
#endif
    // while(con_status);
    //  asm volatile("memb\n");
#ifdef MULTI_SPAWN

    int i;
    for (i = 0; i < num; i++) {
        my_h2ldm(con_task_ptr[i], thread_id, cgid, unsigned long) = (unsigned long)temp_task[i];
        // con_dest_ptr=(unsigned long)t;
        asm volatile("memb\n");
    }

    my_h2ldm(con_task_num, thread_id, cgid, int) = num;
    // my_h2ldm(spawn_status[iter],thread_id,cgid,int)=num;

    asm volatile("memb\n");
// return ;
#else
    my_h2ldm(con_src_ptr, thread_id, cgid, unsigned long) = (unsigned long)t;
#endif
    asm volatile("memb\n");
    // while(con_status);

    // asm volatile("memb\n");
    // con_status=3;

    my_h2ldm(con_status, thread_id, cgid, int) = 3;
    asm volatile("memb\n");
    block_temp = rpcc();
    while (my_h2ldm(con_status, thread_id, cgid, int))
        ;
    block_time += rpcc() - block_temp;
    asm volatile("memb\n");

    return;
}
void my_atomic_sub(volatile int *ref)
{
    my_h2ldm(con_src_ptr, thread_id, cgid, unsigned long) = (unsigned long)ref;
    // con_dest_ptr=(unsigned long)ref;
    asm volatile("memb\n");
    // con_status=5;
    my_h2ldm(con_status, thread_id, cgid, int) = 5;
    asm volatile("memb\n");
    block_temp = rpcc();
    while (my_h2ldm(con_status, thread_id, cgid, int))
        ;
    block_time += rpcc() - block_temp;
    asm volatile("memb\n");
    return;
}
void my_add_successor(struct aceMesh_task *preTask,struct aceMesh_task *T)
{
    //int temp_tail = my_h2ldm(main_tail, total_num_threads - 1, cgid, int);
    //while ((temp_tail + 1) % BLOCK_SIZE == my_h2ldm(main_head, total_num_threads - 1, cgid, int))
      //  ;
    // printf("main_task:%x\n",&(t->task_base));
    //my_h2ldm(main_task[temp_tail], total_num_threads - 1, cgid, unsigned long) =
     //   (unsigned long)preTask;
   // my_h2ldm(main_tail, total_num_threads - 1, cgid, int) = (temp_tail + 1) % BLOCK_SIZE;
    //asm volatile("memb\n");
    my_h2ldm(con_dest_ptr, total_num_threads - 1, cgid, unsigned long) =
       (unsigned long)preTask;
        asm volatile("memb\n");
     my_h2ldm(con_src_ptr, total_num_threads - 1, cgid, unsigned long) =
       (unsigned long)T;
 asm volatile("memb\n");
       while (my_h2ldm(con_src_ptr, total_num_threads - 1, cgid, unsigned long))
        ;  
    asm volatile("memb\n");
}
void my_add_end_successor(struct aceMesh_task *preTask,struct aceMesh_task *T)
{
    // int temp_tail = my_h2ldm(end_tail, total_num_threads - 1, cgid, int);
    // while ((temp_tail + 1) % BLOCK_SIZE == my_h2ldm(end_head, total_num_threads - 1, cgid, int))
    //     ;
    // my_h2ldm(con_task_ptr[temp_tail], total_num_threads - 1, cgid, unsigned long) =
    //     (unsigned long)preTask;
    // my_h2ldm(end_tail, total_num_threads - 1, cgid, int) = (temp_tail + 1) % BLOCK_SIZE;
    // asm volatile("memb\n");
     my_h2ldm(con_dest_ptr, total_num_threads - 1, cgid, unsigned long) =
       (unsigned long)preTask;
        asm volatile("memb\n");
     my_h2ldm(con_src_ptr, total_num_threads - 1, cgid, unsigned long) =
       (unsigned long)T;
        asm volatile("memb\n");
       while (my_h2ldm(con_src_ptr, total_num_threads - 1, cgid, unsigned long))
        ;  
    asm volatile("memb\n");
}
// void my_send_to_agent(struct aceMesh_task *dest, volatile int num) {}
#endif
